Exploration spatio-temporelle d’objets géographiques ponctuels

L’exemple du patrimoine architectural toulousain

Le Texier Marion (Université Rouen Normandie, UMR 6266 IDEES)

Introduction

Cette fiche rend compte d’une série de traitements permettant d’explorer la base de données MERIMEE qui consigne l’ensemble des bâtiments classés aux monuments historiques. Nous prendrons l’exemple des fiches “Mérimée” du patrimoine architectural toulousain disponibles sur le site data.gouv.fr.

1 Préparation de l’analyse

1.1 Librairies utilisées

  • tidyverse regroupe plusieurs librairies pour la manipulation de données, dont dplyr
  • sf permet de réaliser la manipulation et le traitement de données géographiques
  • raster permet la manipulation d’information stockée en mode raster (matrice de pixel)
  • pracma permet de réaliser des lissages par moyennes mobiles de différents types
  • forecast offre un ensemble de fonctions d’ajustement de séries temporelles
  • ggplot2 pour la représentation graphique. C’est une librairie de référence avec sa popre syntaxe
  • ggspatial pour représenter des données spatiales. Syntaxe ggplot2
  • ggmap pour faire des cartes. Syntaxe ggplot2
  • gganimate (et sa dépendance gifski) permet de réaliser des cartes animées
  • cowplot offre des fonctionnalités pour combiner plusieurs graphiques
  • rasterVis permet la visualisation de raster
  • leaflet est une adaptation de la librairie javascript du même nom, permet de réaliser des cartes interactives
  • osmdata permet de télécharger et d’utiliser des données OpenStreetMap
  • RColorBrewer propose l’utilisation et la création de palette de couleurs
  • plotly permet la réalisation de représentations graphiques interactives

Création d’un vecteur comportant tous les noms des packages nécessaires :

my_packages <- c("sf", 
                  "dplyr",
                  "tidyverse", 
                  "pracma", 
                  "forecast",
                  "ggplot2", 
                  "cowplot", 
                  "leaflet", 
                  "raster", 
                  "rasterVis", 
                  "ggspatial", 
                  "ggmap", 
                  "osmdata", 
                  "gganimate", 
                  "gifski", 
                  "RColorBrewer",
                  "plotly")

La fonction installed.packages() liste tous les packages déjà installés. Utilisons-la pour récupérer la liste des packages que vous allez devoir installer.

missing_packages <- my_packages[!(my_packages %in% installed.packages()[,"Package"])]

Utilisons install.packages() pour installer les packages depuis le CRAN (Comprehensive R Archive Network). Le test conditionnel if() vérifie qu’il vous manque bien des packages avant de lancer l’installation.

if(length(missing_packages)) install.packages(missing_packages)

Chargement de toutes les librairies à l’aide de la fonction lapply() qui permet d’appliquer la fonction library() à mon vecteur de noms de packages :

lapply(my_packages, library,  character.only = TRUE)


1.2 Les données


Télécharger les données


Deux couches géographiques en format shape sont mises à votre disposition :

1) communes.shp : Limites des communes de la métropole toulousaine récupérées sur data.gouv.fr


2) base-merimee.shp : Extraction de la BD Mérimée mise à disposition sur data.gouv.fr. Il s’agit d’une couche géographique d’objets ponctuels. Chaque point est un monument du patrimoine architectural toulousain précisément géolocalisé dans l’espace.

Chaque monument est caractérisé par une cinquantaine de variables. Extrait :


1.2.1 Import

Les données géographiques stockées en format shapefiles s’importent très facilement avec la librairie sf.
Ces données sont alors converties en objet sf (simple feature dataframe). Il s’agit tout simplement d’un tableau (dataframe) dans lequel chaque élement/ligne est associé à une géometrie. Ici, la colonne geometry (cf. ci-dessus) liste les coordonnées x/y des monuments localisés dans l’espace par un point.

Utilisez st_read() pour importer les données.

Merimee <- st_read("data/base-merimee.shp")


1.2.2 Caractéristique spatiale

La fonction summary() permet d’obtenir un résumé de l’ensemble du tableau de données ou de certaines variables d’intérêt. Ici, elle permet de décrire la variable geometry qui contient la forme et la localisation de chaque élément du tableau.

summary(Merimee$geometry)
        POINT     epsg:4326 +proj=long... 
         3174             0             0 


1.2.3 Caractéristique temporelle

La fonction colnames() renvoie le nom de l’ensemble des colonnes du tableau de données.

colnames(Merimee)
 [1] "chpuser"     "chpdeno"     "chpgenr"     "chppden"     "chpvoca"    
 [6] "chpappl"     "chpactu"     "chptico"     "chppart"     "chprefp"    
[11] "chpcoll"     "chpreg"      "chpdpt"      "chpcom"      "chpinsee"   
[16] "chpploc"     "chpaire"     "chpcant"     "chplieu"     "chpadrs"    
[21] "chpedif"     "chprefe"     "chpcada"     "chpzone"     "chpcoor"    
[26] "chpcoorm"    "chpcoorlb93" "chpcoormlb9" "chpcoorwgs8" "chpcoormwgs"
[31] "chpimpl"     "chphydr"     "chpscle"     "chpscld"     "chpdate"    
[36] "chpjdat"     "chpautr"     "chpjatt"     "chppers"     "chpremp"    
[41] "chpdepl"     "chphist"     "chpprot"     "chpdpro"     "chpppro"    
[46] "chpapro"     "chpmhpp"     "chpsite"     "chpinte"     "chprema"    
[51] "chpobs"      "geometry"   

La variable nommée chpdate contient les informations sur la date de construction des bâtiments classés aux monuments historiques.

Appliquons les fonctions class(), head() et summary() à cette variable pour en apprendre un peu plus.

class(Merimee$chpdate)
[1] "character"
head(Merimee$chpdate)
[1] NA            "1776 ; 1866" NA            "1883"        "1881"       
[6] NA           
summary(Merimee$chpdate)
   Length     Class      Mode 
     3174 character character 

Cette variable contient de nombreuses valeurs manquantes et plusieurs dates sont parfois renseignées.

Pour ces raisons, R a automatiquement typé la variable en factor (catégorie) et non pas en numeric. Ainsi, la fonction summary() renvoie le nombre d’individus regroupés par catégories (levels) plutôt que de fournir un résumé de ses paramètres centraux et de dispersion.


1.3 Pré-traitements

Dans le cadre de cette exploration, nous allons supprimer les monuments sans date de construction et ne garder que la première date renseignée lorsque plusieurs périodes de construction sont proposées.

La multiplication des opérations sur un même objet peut être facilitée grâce à l’utilisation de tidyverse, un ensemble d’extensions contenues dans une librairie, qui utilise une syntaxe particulière (Sucre syntaxique) qui rend le code plus agréable à écrire comme à lire.

Dans l’exemple ci-dessous, nous modifions la variable chpdate en deux temps :

  1. Extraction des 4 premiers caractères de la variable chpdate avec la fonction substr()

  2. Transformation de cette chaîne de 4 caractères en nombre grâce à la fonction as.integer()

Merimee$chpdate <- substr(Merimee$chpdate, 1, 4) %>%
  as.integer(Merimee$chpdate) 

Puis, nous supprimons les lignes pour lesquelles l’année de construction est inconnue (NA), grâce aux fonctions filter() et is.na(). Notez que l’usage du ! signifie ici “est différent de”.

Merimee <- filter(Merimee, !is.na(Merimee$chpdate))

summary(Merimee$chpdate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1453    1874    1911    1897    1950    2009 

Les bâtiments toulousains classés aux monuments historiques les plus anciens datent de 1453, alors que les plus récents ont été construits en 2009, soit une période de 556 ans.

50% des bâtiments classés ont été construits avant 1911, un quart avant 1874 et un quart après 1950.


2 Exploration temporelle

2.1 Nombre de bâtiments par année

Pour observer la distribution temporelle du patrimoine classé aux monuments historiques à Toulouse, commençons par comptabiliser le nombre de bâtiments par année de construction.

On regroupe les lignes par année, tout en comptabilisant le nombre de lignes regroupées.

Merimee_year <- Merimee %>%
  group_by(chpdate) %>%
  summarize(nb = length(chpdate)) 

summary(Merimee_year)
    chpdate           nb                  geometry  
 Min.   :1453   Min.   : 1.000   MULTIPOINT   :167  
 1st Qu.:1774   1st Qu.: 1.000   POINT        : 95  
 Median :1874   Median : 3.000   epsg:4326    :  0  
 Mean   :1844   Mean   : 4.256   +proj=long...:  0  
 3rd Qu.:1941   3rd Qu.: 6.000                      
 Max.   :2009   Max.   :23.000                      

Il est intéressant de noter que le type d’objet sf est conservé malgré les importantes manipulations réalisées. Les géométries ont même été prises en compte. La variable geometry stocke désormais un ensemble de points (MULTIPOINT) décrivant la localisation de tous les batîments construits une même année.

Les fonctions de la librairie ggplot2 vont nous permettre d’en faire une représentation graphique.

  • ggplot() permet de construire un graphique croisant année de construction et nombre de bâtiments concernés.
  • geom_line() trace une courbe reliant l’ensemble des points du graphique.
  • labs() permet d’ajouter titre, sous-titre, noms des axes…
ggplot(Merimee_year, aes(chpdate, nb)) + geom_line(color = "orange") +
  labs(x = "", y = "Nombre de bâtiments", 
       title = "Répartition des bâtiments classés aux monuments historiques par année de construction") 


2.2 Moyennes mobiles symétriques

Afin de lisser la courbe et de mieux visualiser les tendances, nous allons calculer des moyennes mobiles d’ordre k.

L’estimation de la moyenne en un point par la moyenne des valeurs qui l’entourent nécessite des dates équidistantes. Nous allons donc ajouter les années manquantes au tableau et y associer la valeur 0 (absence de bâtiments classés construits à cette date).

Pour cela, nous créons un tableau all_years contenant la colonne chp_date et recensant l’ensemble des entiers (années) compris entre la valeur minimale (année la plus ancienne) et la valeur maximale (année la plus récente) du tableau Merimee_year.

all_years <- data.frame(chpdate=c(min(Merimee_year$chpdate):max(Merimee_year$chpdate)))

Puis, nous réalisons une Jointure entre les tableaux all_years et Merimee_year en utilisant leur variable identique chp_date et la fonction merge().

Merimee_year <- merge(Merimee_year, all_years, by="chpdate", all = TRUE) 

Les années manquantes ont été ajoutées, mais elles affichent la valeur NA (not available) dans la variable nb. Remplaçons ces valeurs NA par 0, puisqu’aucun batîment construit ces années-là n’a bénéficié de la certification.

Merimee_year$nb[is.na(Merimee_year$nb)] <- 0

Notons que ces nouvelles lignes ne comportent pas non plus de géométrie :


La librairie pracma et sa fonction movavg() permettent de calculer des moyennes mobiles à partir d’une série de valeurs. Les arguments de la fonction sont :

  • x = la série statistique
  • n = amplitude de la fenêtre temporelle (nombre de voisins)
  • type = moyenne mobile à calculer. La valeur “s” correspond à la forme la plus simple. Elle calcule la valeur moyenne des n valeurs précédant la valeur courante en tenant compte de cette dernière.

Il s’agit donc d’un lissage linéaire puisqu’un poids identique est donné à chacune des valeurs considérées.

# Le résultat est stocké dans la variable ave_nb
Merimee_year$ave_nb <- movavg(Merimee_year$nb, 
                              n= 5, 
                              type="s")

Dans un objectif comparatif, calculons des moyennes mobiles selon plusieurs valeurs de voisinage k (5, 10, 20 et 40) et représentons les différentes courbes obtenues côte à côte.

Pour cela nous utiliserons la boucle for() qui permet de reproduire, en boucle, une serie de traitements. A chaque nouvelle boucle, nous modifions la valeur de k et réalisons une représentation graphique qui est enregistrée dans un objet. La fonction assign() personnalise le nom de cet objet ggplot à l’aide de la valeur de k.

for (k in c(5,10,20,40)) {

  # Calcul des moyennes mobiles
  Merimee_year$ave_nb <- movavg(Merimee_year$nb, n= k, type=c("s"))
  
  # Construction du graphique
  Wind_plot <- ggplot(Merimee_year, aes(chpdate,ave_nb)) + 
               geom_line(color="orange") +
               labs(x = "", y="Nombre de bâtiments", subtitle = paste0("Fenêtre temporelle = ", k, " ans")) 
  
  # Spécification du nom avec la valeur de k
  assign(paste0("window_", k), Wind_plot)
}

Une fois que les quatre graphiques ont été construits et enregistrés dans des objets différents, il est aisé de les afficher tous ensemble dans la fenêtre graphique grâce aux fonctions proposées par la librairie cowplot.

# Association des graphiques dans une grille
multi_plots <- plot_grid(window_5,window_10,window_20,window_40)

# Paramétrages titre/mise en page
title_plot <- ggdraw() + 
              draw_label("Répartition du nombre de bâtiments historiques par année de construction",
                          fontface = 'bold', x = 0,
                          hjust = 0) +
             theme(plot.margin = margin(0, 0, 0, 7))


# Affichage de l'ensemble
plot_grid(title_plot, multi_plots,
          ncol = 1,
          rel_heights = c(0.1, 1))


2.3 Lissage exponentiel simple

Le lissage symétrique par moyennes mobiles donne un poids équivalent à l’ensemble des années contenues dans la fenêtre temporelle de dimension k. Une alternative consiste à faire décroître le poids de ces dernières en fonction de leur distance temporelle à la date d’intérêt. Il s’agit d’un lissage exponentiel.

Différents modèles de lissage exponentiel existent. La version la plus simple consiste à estimer une valeur à un temps t de la façon suivante :

\(\hat{y_t} = \alpha y_t + (1-\alpha)y_{t-1} + (1-\alpha)^2 y_{t-2} + (1-\alpha)^3 y_{t-3} + ... etc.\)

La décroissance des pondérations en remontant dans le temps est définie de façon exponentielle. Cette méthode est plus réactive aux courtes variations de tendance.

L’utilisation d’un modèle exponentiel simple semble adapté à la distribution temporelle des années de construction des bâtiments toulousains classés aux monuments historiques puisque celle-ci ne présente pas de saisonnalité ou une tendance unique.

La fonction ses() (simple exponential smoothing) de la librairie forecast permet d’ajuster un modèle exponentiel simple à une serie statistique. L’ajustement consiste à déterminer la valeur du coefficient de lissage \(\alpha\).

# Création d'un objet time series
Merimee_ts <- ts(Merimee_year$nb, start = min(Merimee_year$chpdate), end = max(Merimee_year$chpdate))

Merimee_ts_fit <- ses(Merimee_ts)

Merimee_ts_fit$model
Simple exponential smoothing 

Call:
 ses(y = Merimee_ts) 

  Smoothing parameters:
    alpha = 0.292 

  Initial states:
    l = 0.2912 

  sigma:  2.1595

     AIC     AICc      BIC 
4383.285 4383.329 4396.253 

La valeur du paramètre \(\alpha\) est de 0.29, ce qui signifie que 29% des prédictions sont basées sur l’observation la plus récente. Les données anciennes ont un poids relativement important sur les valeurs prédites à un temps t (\(\alpha\) est plus proche de 0 que de 1), alors que les changements récents ont un impact moindre sur les valeurs prédites.

En appliquant la fonction summary() sur les résultats de la fonction ses(), on peut afficher les valeurs prédites (forecats) pour les 10 intervalles de temps (ici année) suivant la dernière date de sa série statistique (soit de 2010 à 2019).

summary(Merimee_ts_fit)

Forecast method: Simple exponential smoothing

Model Information:
Simple exponential smoothing 

Call:
 ses(y = Merimee_ts) 

  Smoothing parameters:
    alpha = 0.292 

  Initial states:
    l = 0.2912 

  sigma:  2.1595

     AIC     AICc      BIC 
4383.285 4383.329 4396.253 

Error measures:
                      ME     RMSE      MAE  MPE MAPE      MASE         ACF1
Training set 0.007292547 2.155576 1.117794 -Inf  Inf 0.8853182 -0.006887626

Forecasts:
     Point Forecast     Lo 80    Hi 80     Lo 95    Hi 95
2010       1.477131 -1.290324 4.244586 -2.755326 5.709588
2011       1.477131 -1.405872 4.360134 -2.932041 5.886303
2012       1.477131 -1.516963 4.471226 -3.101941 6.056204
2013       1.477131 -1.624078 4.578341 -3.265759 6.220022
2014       1.477131 -1.727615 4.681877 -3.424105 6.378367
2015       1.477131 -1.827910 4.782172 -3.577493 6.531755
2016       1.477131 -1.925249 4.879512 -3.726361 6.680623
2017       1.477131 -2.019881 4.974143 -3.871087 6.825349
2018       1.477131 -2.112018 5.066280 -4.011998 6.966261
2019       1.477131 -2.201848 5.156110 -4.149382 7.103644

autoplot() et autolayer() permettent d’afficher graphiquement la qualité de l’ajustement du modèle aux données observées.

autoplot(Merimee_ts_fit) +
  autolayer(Merimee_ts_fit$fitted, color="red", lwd=1)

La représentation graphique d’un modèle de lissage exponentiel présente par défaut les valeurs prédites par ce dernier. En mauve clair figure ainsi l’intervalle de confiance à 95% et en mauve foncé l’intervalle de confiance à 80 %.

Les prédictions par lissage exponentiel simple n’opèrent que pour un futur proche. Le graphique montre par ailleurs que ces valeurs sont estimées à un certain niveau (95% de chance qu’il n’y ait pas plus de 7 bâtiments construits après 2009 classés aux monuments historiques) mais de façon plate, puisque ce type de lissage ne tient compte d’aucune tendance ni saisonnalité.


3 Exploration spatiale

3.1 Carte d’inventaire interactive

La réalisation d’une carte d’inventaire interactive avec la librairie Leaflet permet de positionner chaque bâtiment dans l’espace toulousain et d’associer à chacun des points une fenêtre interactive.

Leaflet propose d’afficher le fond de carte principal d’OpenStreetMap et sa projection par défaut (web Mercator, code epsg:4326). Il est donc parfois nécessaire de transformer le système de coordonnées de référence des données géographiques.

Pour l’ajout de labels personnalisés, nous utiliserons l’identifiant du bâtiment classé (chpuser), le type de bâtiment (chpdeno), le siècle de construction (chpscle), le numéro et nom de rue (chpadrs) et le nom de commune (chpcom).

Commençons par créer la variable qui contient le texte à afficher dans la fenêtre interactive.

Merimee$label <- paste0("<b> Identifiant du bâtiment :</b> ", Merimee$chpuser, "<br>",
                        "<b> Type de bâtiment :</b> ", Merimee$chpdeno, "<br>",
                        "<b> Période de construction :</b> ", Merimee$chpscle, "<br>",
                        "<b> Adresse :</b> ", Merimee$chpadrs, ", ", Merimee$chpcom, "<br>")

La fonction leaflet(), associée à un ensemble de fonctions complémentaires permet de générer la carte. Quelques exemples :

  • addTiles() pour ajouter un fond de carte
  • addMarkers() pour ajouter des marqueurs ponctuels de localisation
  • addScaleBar() pour ajouter une échelle
  • addMiniMap() pour ajouter une surface de localisation

Ces fonctions présentent de nombreux arguments paramétrables. On notera que l’utilisation de pipe (%>%), propre à la syntaxe tidyverse, est particulièrement utile ici pour clarifier la chaîne de traitement.

Merimee_map <- leaflet() %>%
               addTiles() %>%
               addMarkers(
                  lng = unlist(map(Merimee$geometry,1)),
                  lat = unlist(map(Merimee$geometry,2)),
                  data = Merimee,
                  popup = Merimee$label,
                  icon = list(iconUrl="https://upload.wikimedia.org/wikipedia/commons/1/10/Logo-monument-historique.png",
                              iconSize = 25),
                  clusterOptions = markerClusterOptions()) %>%
               setView(lng = 1.433333, lat = 43.6, zoom = 13) %>%
      addScaleBar(position = "bottomright", options = scaleBarOptions(metric = T, imperial = F)) %>%
      addMiniMap()


Merimee_map


3.2 Carte de densité carroyée

Il peut être intéressant de visualiser l’intensité des bâtiments classés aux monuments historiques dans la métropole toulousaine sous la forme d’une cartographie thématique. Plusieurs solutions existent : cartes de densité de points, interpolation spatiale, modèles gravitaires de position, etc.

Si l’on ne souhaite pas estimer des valeurs d’intensité de présence (ce qui nécessite de définir quels sont le voisinage, le type de distance, et la fonction d’impédence de cette dernière les plus opportuns à considérer dans le cas étudié), une solution simple consiste à subdiviser l’espace d’étude par une grille régulière (carroyage) et de comptabliser le nombre de monuments localisés dans chaque carreau.

La librairie raster nous permet de construire la grille, puis de comptabiliser les monuments.

# Construction de la grille régulière
# à partir de l'étendue géographique  couverte par la localisation des bâtiments
Merimee_grid <- raster(extent(Merimee),
                       crs = st_crs(Merimee)$proj4string, nrow=50, ncol=50)

# Calcul du nombre de points par carreau
Merimee_count <- rasterize(Merimee, Merimee_grid, field = 1, fun = "sum")

Evidemment la taille de la grille et son positionnement sur l’espace altèrent le décompte. Comme pour toute analyse exploratoire de données spatiales (ESDA en anglais), il est alors recommandé de multiplier les points de vue (c’est-à-dire d’observer les changements induits par une modification de la grille).

Le résultat est un raster (matrice de pixel).

class(Merimee_count)
[1] "RasterLayer"
attr(,"package")
[1] "raster"

Chaque pixel (carreau) est qualifié par le nombre de bâtiments classés qu’il contient. Il s’agit d’une variable quantitative de stock, mais nous pouvons interpréter cette variable comme une densité (variable quantitative relative) car les valeurs sont calculées sur des surfaces identiques.

Réalisons une carte choroplèthe pour représenter la densité (carroyée) des monuments historiques à Toulouse.

  1. Import de la couche géographique des communes de la métropole toulousaine
Communes_metropole <- st_read("data/communes.shp")
  1. Sélection des limites communales de la ville de Toulouse
# Sélection par le code INSEE
Toulouse_boundary <- Communes_metropole[Communes_metropole$code_insee==31555,]
  1. Utilisation de fonctions des librairies rasterVis et ggplot2 pour la cartographie
gplot(Merimee_count) + 
  geom_tile(aes(fill = value)) +
  scale_fill_gradient(low = "light salmon", high = "dark red", na.value="white") +
  coord_equal() +
  labs(title = "Densité de bâtiments classés à Toulouse", 
       subtitle = "Source : Fiches Mérimée du patrimoine architectural, 2019",
       fill = "Nombre") +
  theme_void() + 
  layer_spatial(Toulouse_boundary, fill=NA) +
  annotation_scale(location = "bl") +
  annotation_north_arrow(location = "tl")


3.3 Carte animée

La représentation cartographique de données spatio-temporelles pose de nombreux challenges, comme en témoigne la large bibliographie portant sur le sujet. L’un des outils offert au chercheur est l’animation cartographique, particulièrement populaire sur le web. Il s’agit de réaliser une carte par pas de temps, puis de les faire se succéder à l’écran chronologiquement.

Nous allons réaliser une animation par changement de localisation. Nous garderons la même symbologie tout au long de l’animation et regroupons les entités cartographiées par année de construction.

Pour réaliser une carte animée représentant les bâtiments classés par année de construction, nous utilisons la librairie ggmap.

La fonction get_map() permet de récupérer des fonds de carte (Google Maps, OpenStreetMap, Stamen Maps…). getbb() nous permet alors de préciser l’emprise géographique (bounding box) souhaitée à partir d’un nom de lieu. Nous précisons également un niveau de zoom.

# Récupération d'un fond de carte
toulouse_map <- get_map(getbb("Toulouse"), maptype = "terrain", zoom = 12)

La fonction ggmap() permet de réaliser des cartographies et s’utilise avec la syntaxe ggplot2. On commence par ajouter tous les bâtiments (points).

ggmap(toulouse_map) +
  geom_point(data=Merimee,
             aes(x=unlist(map(Merimee$geometry,1)),
                 y=unlist(map(Merimee$geometry,2))), 
             col="tomato",size=1, shape=20)

Puis on utilise la fonction transition_time() de la libraire gganimate (syntaxe ggplot2) pour réaliser une carte animée des bâtiments par année de construction. Nous indiquons chpdate comme variable temporelle à utiliser.

La fonction shadow_mark() permet de contrôler l’affichage des entités antérieures et postérieures au pas de temps en cours et ease_aes() gère l’affichage des données durant les transitions (apparition/disparition).

ggmap(toulouse_map) +
  geom_point(data=Merimee,
             aes(x=unlist(map(Merimee$geometry,1)),
                 y=unlist(map(Merimee$geometry,2))),
             col="tomato",size=1, shape=20) +
  transition_time(Merimee$chpdate) +
  labs(title = "Year: {frame_time}") +
  shadow_mark(alpha = 0.3, size = 0.5) +
  ease_aes("linear")


4 Regroupements spatio-temporels

4.1 Prisme spatio-temporel

L’observation des proximités spatiales et temporelles des bâtiments classés aux monuments historiques peut se faire via une représentation en 3 dimensions où les objets sont positionnés sur les axes x et y en fonction de leurs coordonnées spatiales et sur l’axe z selon leur année de construction.

La librairie plotly permet de réaliser des visualisations interactives qui facilitent l’exploration.

plot_ly(x = unlist(x = map(Merimee$geometry,1)), 
               y = unlist(map(Merimee$geometry,2)), 
               z = as.numeric(Merimee$chpdate),
               color= as.numeric(Merimee$chpdate),
               size=0.5) %>% add_markers() %>% 
layout(scene = list(xaxis = list(title = 'longitude'),
                    yaxis = list(title = 'latitude'),
                    zaxis = list(title = 'Année de construction')))


4.2 ST-DBSCAN

Les méthodes de groupement d’événements en fonction de leur proximité spatio-temporelle sont nombreuses (Ansari et al. 2019).

Birant, Kut (2007) proposent d’étendre la méthode de l’algorithme de détection de clusters à partir de l’analyse des densités DBSCAN aux bases de données spatio-temporelles. Ce nouvel algorithme, nommé ST-DBSCAN, reprend la logique du premier : balayer l’espace, compter pour chaque objet le nombre d’objets localisés dans une région d’un certain rayon de voisinage, et considérer que les objets contenus dans une région bénéficiant d’une densité supérieure à un certain seuil appartiennent à un même cluster, à ceci près qu’il introduit également un critère de proximité temporelle.

Les deux auteurs ont développé une fonction R permettant d’appliquer l’algorithme ST-DBSCAN, dont le code est mis à disposition sur Github. Copier-coller l’intégralité du code de leur fonction stdbscan() :

########################################################################
# ST-DBSCAN : An algorithm for clustering spatial-temporal data        #
# (Birant and Kut, 2006)                                 #
# Application on a trajectory                           #
# https://github.com/Kersauson/ST-DBSCAN/blob/master/stdbscan.R #
########################################################################


########################################################################
# INPUTS :                                                             #
# traj = traj gps (x, y and time)                                      #
# eps = distance minimum for longitude and latitude                    #
# eps2 =  distance minimum for date                                    #
# minpts = number of points to consider a cluster                      #
########################################################################

stdbscan = function(traj,
                    x,
                    y,
                    time,
                    eps,
                    eps2,
                    minpts,
                    cldensity = TRUE) {

  countmode = 1:length(x)
  seeds = TRUE

  data_spatial<- as.matrix(dist(cbind(y, x)))
  data_temporal<- as.matrix(dist(time))
  n <- nrow(data_spatial)

  classn <- cv <- integer(n)
  isseed <- logical(n)
  cn <- integer(1)

  for (i in 1:n) {
    if (i %in% countmode)
      #cat("Processing point ", i, " of ", n, ".\n")
      unclass <- (1:n)[cv < 1]

    if (cv[i] == 0) {
      reachables <- intersect(unclass[data_spatial[i, unclass] <= eps],  unclass[data_temporal[i, unclass] <= eps2])
      if (length(reachables) + classn[i] < minpts)
        cv[i] <- (-1)
      else {
        cn <- cn + 1
        cv[i] <- cn
        isseed[i] <- TRUE
        reachables <- setdiff(reachables, i)
        unclass <- setdiff(unclass, i)
        classn[reachables] <- classn[reachables] + 1
        while (length(reachables)) {
          cv[reachables] <- cn
          ap <- reachables
          reachables <- integer()

          for (i2 in seq(along = ap)) {
            j <- ap[i2]

            jreachables <- intersect(unclass[data_spatial[j, unclass] <= eps], unclass[data_temporal[j, unclass] <= eps2])

            if (length(jreachables) + classn[j] >= minpts) {
              isseed[j] <- TRUE
              cv[jreachables[cv[jreachables] < 0]] <- cn
              reachables <- union(reachables, jreachables[cv[jreachables] == 0])
            }
            classn[jreachables] <- classn[jreachables] + 1
            unclass <- setdiff(unclass, j)
          }
        }
      }
    }
    if (!length(unclass))
      break

  }


  if (any(cv == (-1))) {
    cv[cv == (-1)] <- 0
  }
  out <- list(cluster = cv, eps = eps, minpts = minpts, density = classn)
  rm(classn)
  if (seeds && cn > 0) {
    out$isseed <- isseed
  }
  class(out) <- "stdbscan"
  return(out)
}

Executer le code. Vous pouvez désormais utiliser cette fonction de partitionnement comme n’importe quelle autre fonction.

Nous indiquons ici les coordonnées en longitude et latitude des bâtiments classés, leur date de construction, les seuils de distance spatiale euclidienne et de distance temporelle, et la valeur à partir de laquelle considérer un ensemble de points comme un cluster.

Merimee$lon <- unlist(map(Merimee$geometry,1))
Merimee$lat <- unlist(map(Merimee$geometry,2))

spatiotemp_clust <- stdbscan(x = Merimee$lon, 
                             y = Merimee$lat, 
                             time = Merimee$chpdate,
                             eps = 1, 
                             eps2 = 1, 
                             minpts = 10)


spatiotemp_clust
$cluster
   [1] 0 1 1 2 3 3 3 0 4 0 2 4 1 1 1 1 4 3 3 3 1 3 1 4 1 0 0 1 3 2 1 3 1 4 1 0 1
  [38] 0 4 4 3 1 4 1 1 1 2 1 3 4 3 1 3 5 2 6 1 4 0 2 0 0 0 4 0 0 2 0 1 4 2 1 6 4
  [75] 0 1 0 1 3 4 4 0 0 1 1 1 3 3 1 3 4 4 1 3 3 3 0 5 2 3 2 2 1 4 4 1 4 0 3 1 0
 [112] 2 1 1 1 3 4 4 0 4 0 4 5 1 3 1 3 1 3 1 5 4 4 1 4 4 0 5 1 4 0 4 2 3 3 4 1 3
 [149] 3 1 1 2 1 4 3 4 0 1 3 0 1 0 0 0 1 3 1 3 3 1 6 3 2 0 1 2 1 4 4 1 1 3 0 4 0
 [186] 2 0 2 0 3 0 1 4 6 0 4 3 0 1 1 1 5 1 3 1 1 0 1 0 6 4 4 0 2 1 3 4 1 1 1 3 3
 [223] 1 3 3 2 3 3 2 1 3 0 1 4 1 3 4 3 0 1 1 3 3 2 1 0 3 1 3 1 1 5 0 0 1 4 3 1 1
 [260] 4 4 1 3 1 1 6 1 1 1 4 3 1 4 4 1 6 0 0 4 1 1 4 4 4 1 0 3 6 4 4 1 0 0 4 3 3
 [297] 3 4 0 4 2 7 3 2 4 0 0 1 2 3 2 1 4 0 4 4 1 3 1 0 1 1 4 4 1 4 1 0 1 0 0 1 3
 [334] 3 1 1 3 1 0 3 4 3 4 2 1 2 0 1 0 2 3 0 3 2 0 2 4 3 3 1 0 3 1 1 3 3 3 1 1 4
 [371] 1 1 3 1 4 4 4 0 7 1 3 1 3 4 4 4 4 4 1 1 4 4 4 1 4 3 1 7 0 4 0 3 1 4 4 1 4
 [408] 3 2 4 4 3 1 1 4 1 3 3 4 0 0 1 0 0 1 1 1 5 3 0 0 4 1 0 3 1 3 3 4 0 3 1 4 2
 [445] 4 0 4 3 3 0 1 0 4 3 1 3 3 3 1 4 4 4 3 1 4 3 3 0 1 1 1 1 1 3 1 1 1 0 3 0 1
 [482] 1 4 1 4 1 1 0 1 1 4 1 3 0 1 3 5 4 3 4 0 0 3 0 0 1 1 1 4 1 2 3 3 1 4 0 1 0
 [519] 5 4 1 4 4 0 1 3 3 3 3 1 4 2 1 1 4 1 4 0 4 1 1 4 3 3 4 2 1 3 3 3 3 1 3 1 1
 [556] 3 1 1 3 0 4 0 4 0 1 1 0 3 0 3 2 1 3 4 3 3 1 1 1 0 1 1 4 3 1 3 1 1 0 3 0 0
 [593] 1 5 4 1 4 4 1 0 4 4 3 4 1 2 0 4 4 0 2 1 0 0 0 1 1 4 4 1 6 3 4 0 0 2 0 4 1
 [630] 1 6 0 4 3 4 1 0 3 3 1 1 1 1 3 3 4 0 1 1 1 1 4 6 1 1 0 1 3 3 1 0 6 3 4 3 1
 [667] 3 1 1 1 4 1 0 3 3 0 0 7 7 1 3 1 0 0 1 3 3 4 1 1 2 1 0 3 1 0 0 3 4 4 7 4 0
 [704] 1 1 0 4 4 0 3 1 0 4 1 1 4 1 2 3 4 4 4 3 0 3 4 3 2 3 5 5 1 3 1 4 1 4 1 3 3
 [741] 1 3 1 1 1 1 0 1 0 5 1 3 3 0 3 1 6 1 1 3 0 1 4 1 3 0 4 1 1 3 3 1 1 1 1 1 0
 [778] 4 4 1 3 1 0 0 1 5 0 3 3 0 4 4 1 4 1 0 1 2 4 1 4 4 3 1 1 3 1 0 7 7 0 1 0 1
 [815] 1 0 1 5 0 2 0 0 2 1 1 3 1 3 0 1 0 1 5 3 2 4 2 4 0 2 1 4 1 3 1 1 0 5 2 0 1
 [852] 0 3 2 1 1 4 2 1 1 4 1 1 1 4 0 0 0 1 1 3 3 5 1 1 1 4 3 1 0 1 2 0 3 1 4 0 3
 [889] 3 0 3 3 2 0 1 3 3 0 2 7 7 0 2 3 1 0 3 1 7 1 3 1 4 4 1 4 0 1 3 2 4 2 2 0 4
 [926] 1 3 3 4 2 4 3 4 4 2 4 1 3 1 1 3 1 1 4 0 4 4 0 4 7 1 3 1 1 0 0 0 4 3 0 1 1
 [963] 4 3 3 0 3 3 1 3 1 3 1 2 0 0 2 1 1 0 3 4 0 0 4 1 5 4 1 4 1 4 0 3 2 3 3 4 3
[1000] 1 3 3 1 3 1 0 3 3 1 4 0 4 4 4 4 4 2 1 0 2 3 4 0 3 2 4 4 5 4 1 1 4 6 0 3 1
[1037] 2 4 4 4 1 3 0 0 5 0 1 4 3 1 2 3 4 4 1 1 0 1 1 3 5 1 5 6 1 1 1 4 2 0 4 5 1
[1074] 3 1 3 1 0 0 1 6 1 1 1 1 5 1 1 1 4 1 1 2 2 3 1 4 4 4 0 5 4 1 3 4 1 3 4 1 1
[1111] 4 0 0 3 1

$eps
[1] 1

$minpts
[1] 10

$density
   [1]  0  0  9  0  0  2  7  0  0  0  2 14  2  5  3 10  5  4  8  7  5  7  7 18
  [25]  5  0  0 16  4 15  6  8 10 19  7  0  8  0  6 16  3  6  6  7  4 14  3  8
  [49]  8 10  6  3  3  0  4  0  8 15  0 16  0  0  0  2  0  0  5  0  5  9 12  5
  [73]  2  7  0  5  0  9 12 20 13  0  0  8  5 11  6  6  5  9 15  9  8  9  5  8
  [97]  0  5  6  8  7  9  6 10 21 10  7  0 13  6  0  3  3  7  8  5 11 11  0 16
 [121]  0  7  2 11  8  9  7 18 10  8  2 17  9 11 10 17  0  3 12  7  0  9 17  6
 [145]  5 11 11  8  8  7  7  4  5  4  7  9  0  6 10  0 12  0  0  0  6  7  9  9
 [169]  9 19  7  6 18  0  2 16 13 10  3 20 11 24  0 10  0 10  0 19  0  8  0  4
 [193] 24  3  0 14  6  0  7  9  9  4 10 11 15  2  0  9  0  8 15  2  0 11  8 11
 [217]  5  7  8 21 10  7  7 10 13 10  7  5 12  9  9  0  3 16  8  7 11  8  0  8
 [241] 10  9 10 11  9  0 11 10 14  4  8  5  0  0 16 17  9  7 10  5 18  9  7 10
 [265]  9  4  7 11  5  8  4 12  6  7 12  5  4  0 11  6 11  8 18  4 17  0 10  6
 [289] 11 12 10  0  0 25  5  6  7 12  0 12 17  3  7 20 12  0  0 11  8 11 15  8
 [313] 19  0 13 14 18 10 13  0  9 19  8  7 10 17  9  0 20  0  4 14 12  8 12 12
 [337] 14  9  0 15  9 13 13 10  8 11  0 11  0 18 14  0 12 13  0 19  5 15 11  5
 [361]  0  9 14  8  8  8  7  4 13 22 12  6  9  8  5 16  6  0  0 13 11  3  9 12
 [385]  7 18  8  6  7  4 19  6  9 12 15 12 14  2  0 10  0 13  5 10 14 15 20  9
 [409] 20 10 11 12 13 16  8 11 11 10 21  0  0  4  0  0 11 11 12  6 15  0  0  7
 [433] 15  0  6  9 25  6 12  0  8  9 12 12 11  1  7 12  7  0 10  0 16 16 10 17
 [457] 10  8 13 17 18  8 13 10 17 10 13  0 14 17  7 12 11 14 12 10  9  0 18  0
 [481]  6 11 13 14 26 12  4  0 13  5 18 15 14  0  9  9  5 27  7 13  0  0 13  0
 [505]  3 18 10 10 19 21 13 16 11 11  9  0  9  0  3 23  5 14 22  0 10 10 16 15
 [529] 12  5  7 20  6  8 28 14 14  0  9 19 11  9  9  9 15 21 16  8 10 13 12 14
 [553]  9  9 10  9  7 13 11  4 16  0 15  0  8 17  0 19  0 11  2  9  4 18  9 13
 [577] 16 21 10  0  5 11 16 20 16 26 19 10  0 21  0  0 17  8 20  8 11 13 14  0
 [601] 13 17  9 29 10 21  0  8 19  0 11 20  0  0  0 11 21 24 18  6  9  9 14  0
 [625]  0 22  0 17 12 11  7  0 18 12 30 17  0  7 17 12  7 24  9  9 27 23  0 14
 [649] 11 19 11 21  8 22 11  0 22 11 10 11  0  8 10 19 17 13 14 10 13 20 14 17
 [673]  0 11 22  0  0  3  4 12 11 13  0  0 21 14  8 19 11 13 12 12  0 11 11  0
 [697]  0 10 22 20  5 12  0 11 28  0 20 10  0 15 18  0 21 15 23 11 19 23 23 20
 [721] 21  9 11  0  7 10  7 13 10  7  7  7  8 15 20  9 13 13 13 15  6 16 30 12
 [745] 10 31  0 23  0  6 22 11 12  0 24 23  9  7 24  5  0  6 22 12 12  4 15 12
 [769] 12 10 11 21 16  8  8 14  0 23 10  5 28 24  0  0 13  5  0 12 12  0  9 10
 [793] 14 24 14  0  6 15 15 14 11 12  9 20 13 12 13  0  6  7  0 13  0 10 21  3
 [817] 15  8  0 22  0  0 24 18 25  8 15  6  0 12  0 13 12 14 22 19 25 12  0 15
 [841]  9 13 14 15 14 16  0  7 24  0 26  0 10 23  6 14 25 12 16 10 26 14 17 26
 [865] 13  0  0  0 17  8 29  8  8 18 18 11 14 10 13  0 15 14  0 13 13 14  0 30
 [889] 16  0  7 15 25  0 27 10 14  0 26  8  9  0 15 18 11  0 11 15  3  9 14 12
 [913] 16  8 22 10  0  7 16 24 22 19 27  0 31  7 12 16 21 20 15 11 11 23 21 27
 [937] 23 12 15 19 17 10 20 19  0 12 22  0 11 10 36 31 37 14  0  0  0 23  8  0
 [961] 22 19 25 11 15  0  8 12 16  9  8 12 16 16  0  0 29 18 12  0 11 28  0  0
 [985] 23  7  9 21 30 16 12 17  0 18 25 17 16 24 25 12 14 13 39 15 12  0 26 18
[1009] 15 17  0 16 29 18 13 25 23 17  0 28 10 17  0 32 19 13 24 10 20  8 13 30
[1033] 10  0 13 14  3 18 15 19  9 18  0  0  8  4 10 12 17 40 24 27 14 14  9 10
[1057]  0 27 19 12 15 15 11 10 16 12 33 20 26  0 12 12 14 19 34 13 20  0  0  6
[1081] 11 14 35 15 24 11 21 10 13 15 17 10 29 25 19  7 24 18 25  0 12 26 15 28
[1105] 31 11 29  8 13 13 24  0  3 30 17

$isseed
   [1] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE
  [13]  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
  [25]  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
  [37]  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
  [49]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
  [61] FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE
  [73]  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE
  [85]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
  [97] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
 [109]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
 [121] FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [133]  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
 [145]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [157] FALSE  TRUE  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE
 [169]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [181]  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE
 [193]  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [205]  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
 [217]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
 [229]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
 [241]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [253] FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [265]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [277] FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
 [289]  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
 [301]  TRUE FALSE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
 [313]  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
 [325]  TRUE  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
 [337]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
 [349] FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
 [361] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [373]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
 [385]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [397]  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [409]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
 [421] FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE
 [433]  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
 [445]  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
 [457]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
 [469]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE
 [481]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE
 [493]  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE FALSE  TRUE FALSE
 [505] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
 [517]  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
 [529]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
 [541]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [553]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE
 [565]  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
 [577]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [589] FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
 [601]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE
 [613] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
 [625] FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
 [637] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
 [649]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
 [661] FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [673] FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE
 [685]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE
 [697] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE
 [709] FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [721]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [733]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [745]  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
 [757]  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
 [769]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE
 [781]  TRUE  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE  TRUE
 [793]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [805]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE
 [817]  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [829] FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
 [841]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE
 [853]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [865]  TRUE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
 [877]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE
 [889]  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
 [901]  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
 [913]  TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
 [925]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [937]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE
 [949]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE  TRUE  TRUE FALSE
 [961]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [973]  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE
 [985]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
 [997]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
[1009]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
[1021]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[1033]  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE
[1045]  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[1057] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[1069]  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE
[1081]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[1093]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
[1105]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE

attr(,"class")
[1] "stdbscan"

Pour récupérer la classification stockée dans la variable cluster, il suffit d’assigner les valeurs dans une nouvelle variable de notre base de données Mérimée.

Merimee$cluster <- spatiotemp_clust$cluster

Représentons de nouveau les données en trois dimensions et la classification calculée à l’aide de la librairie plotly.

# Création d'une palette de couleurs 
getPalette <- colorRampPalette(brewer.pal(7, "Set1"))

plot_ly(x = unlist(x = map(Merimee$geometry,1)), 
               y = unlist(map(Merimee$geometry,2)), 
               z = as.numeric(Merimee$chpdate),
               color= as.factor(Merimee$cluster),
                colors = getPalette(length(unique(Merimee$cluster))),
               size=0.6) %>% add_markers() %>% 
layout(scene = list(xaxis = list(title = 'Longitude'),
                    yaxis = list(title = 'Latitude'),
                    zaxis = list(title = 'Année de construction')))


Bibliographie

ANSARI, Mohd Yousuf, AHMAD, Amir, KHAN, Shehroz S., BHUSHAN, Gopal et MAINUDDIN, 2019. Spatiotemporal clustering: a review. In : Artificial Intelligence Review [en ligne]. 15 juillet 2019. DOI 10.1007/s10462-019-09736-1. Disponible à l'adresse : https://doi.org/10.1007/s10462-019-09736-1.

BIRANT, Derya et KUT, Alp, 2007. ST-DBSCAN: An algorithm for clustering spatial–temporal data. In : Data & Knowledge Engineering [en ligne]. 2007. Vol. 60, n° 1, pp. 208‑221. DOI https://doi.org/10.1016/j.datak.2006.01.013. Disponible à l'adresse : https://www.sciencedirect.com/science/article/pii/S0169023X06000218.


Annexes

Info session

setting value
version R version 4.0.2 (2020-06-22)
os Windows 10 x64
system x86_64, mingw32
ui RTerm
language (EN)
collate French_France.1252
ctype French_France.1252
tz Europe/Paris
date 2021-09-29
package ondiskversion source
cowplot 1.0.0 CRAN (R 4.0.0)
dplyr 1.0.7 CRAN (R 4.0.5)
forcats 0.5.1 CRAN (R 4.0.5)
forecast 8.12 CRAN (R 4.0.0)
gganimate 1.0.5 CRAN (R 4.0.0)
ggmap 3.0.0 CRAN (R 4.0.0)
ggplot2 3.3.5 CRAN (R 4.0.2)
ggspatial 1.1.1 CRAN (R 4.0.0)
gifski 0.8.6 CRAN (R 4.0.0)
lattice 0.20.41 CRAN (R 4.0.2)
latticeExtra 0.6.29 CRAN (R 4.0.0)
leaflet 2.0.3 CRAN (R 4.0.0)
osmdata 0.1.3 CRAN (R 4.0.0)
plotly 4.9.4.1 CRAN (R 4.0.5)
pracma 2.2.9 CRAN (R 4.0.0)
purrr 0.3.4 CRAN (R 4.0.0)
raster 3.4.13 CRAN (R 4.0.5)
rasterVis 0.47 CRAN (R 4.0.0)
RColorBrewer 1.1.2 CRAN (R 4.0.0)
readr 1.4.0 CRAN (R 4.0.5)
sf 1.0.1 CRAN (R 4.0.5)
sp 1.4.5 CRAN (R 4.0.5)
stringr 1.4.0 CRAN (R 4.0.0)
tibble 3.1.4 CRAN (R 4.0.5)
tidyr 1.1.3 CRAN (R 4.0.5)
tidyverse 1.3.1 CRAN (R 4.0.5)

Citation

Marion Le Texier (2021). “Exploration spatio-temporelle d’objets, géographiques ponctuels.” <URL:, https://rzine.fr/docs/20200601_mletexier86_explo_spatiotemporel/index.html>.

BibTex :

@Misc{,
  title = {Exploration spatio-temporelle d’objets géographiques ponctuels},
  author = {{Marion Le Texier}},
  journal = {Rzine},
  publisher = {FR CIST},
  year = {2021},
  url = {https://rzine.fr/docs/20200601_mletexier86_explo_spatiotemporel/index.html},
}



licensebuttons cc